﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace Highlearner_API
{
	[Serializable]
	public class FolderItem : ISerializable
	{
		public String Name;

		private bool m_bDownload = true;
		public bool Download
		{
			get
			{
				return m_bDownload;
			}
			set
			{
				FileItem l_oFile;
				FolderItem l_oFolder;

				m_bDownload = value;

				for (int i = 0; i < Files.Count; i++)
				{
					l_oFile = Files.Values[i];
					l_oFile.Download = value;
				}
				for (int i = 0; i < Folders.Count; i++)
				{
					l_oFolder = Folders.Values[i];
					l_oFolder.Download = value;
				}
			}
		}

		public SortedList<String, FolderItem> Folders = new SortedList<String, FolderItem>();
		public SortedList<String, FileItem> Files = new SortedList<String, FileItem>();

		public FolderItem(String a_sFolderName)
		{
			Name = a_sFolderName;	
		}

		//Deserialization constructor.
		public FolderItem(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			Name = (String)a_oInfo.GetValue("Name", typeof(String));
			Download = (Boolean)a_oInfo.GetValue("Download", typeof(Boolean));

			Folders = (SortedList<String, FolderItem>)a_oInfo.GetValue("Folders", typeof(SortedList<String, FolderItem>));
			Files = (SortedList<String, FileItem>)a_oInfo.GetValue("Files", typeof(SortedList<String, FileItem>));
		}

		//Serialization function.
		public void GetObjectData(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			a_oInfo.AddValue("Name", Name);
			a_oInfo.AddValue("Download", Download);

			a_oInfo.AddValue("Folders", Folders, typeof(SortedList<String, FolderItem>));
			a_oInfo.AddValue("Files", Files, typeof(SortedList<String, FileItem>));
		}

		public override String ToString()
		{
			return Name;
		}

		public FolderItem GetFolder(String Path)
		{
			String[] l_asFolders = Path.Split('\\');

			FolderItem CurrentFolder = this;

			foreach (String l_sFolder in l_asFolders)
			{
				CurrentFolder = CurrentFolder.Folders[l_sFolder];
			}

			return CurrentFolder;
		}
		public int CountFiles(bool a_bIncludeInnerFolders, bool a_bDownloadEnabled)
		{
			int l_iCount = 0;

			if (a_bIncludeInnerFolders)
			{
				foreach (FolderItem l_oFolder in Folders.Values)
				{
					l_iCount += l_oFolder.CountFiles(a_bIncludeInnerFolders, a_bDownloadEnabled);
				}
			}

			if (a_bDownloadEnabled)
			{
				foreach (FileItem l_oFile in Files.Values)
				{
					if (l_oFile.Download)
					{
						l_iCount++;
					}
				}

				return l_iCount;
			}
			else
			{
				return l_iCount + Files.Count;
			}
		}
	}

	[Serializable]
	public class FileItem : ISerializable
	{
		public String Name;
		public bool Download = true;

		public FileItem(String a_sFileName)
		{
			Name = a_sFileName;
		}

		//Deserialization constructor.
		public FileItem(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			Name = (String)a_oInfo.GetValue("Name", typeof(String));
			Download = (Boolean)a_oInfo.GetValue("Download", typeof(Boolean));
		}

		//Serialization function.
		public void GetObjectData(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			a_oInfo.AddValue("Name", Name);
			a_oInfo.AddValue("Download", Download);
		}

		public override String ToString()
		{
			return Name;
		}
	}

	/// <summary>
	/// Contains information about the course.
	/// Basically, it has some properties, and a files & folders tree with all the course information
	/// The information for each TreeNode is saved in the TreeNode.Tag property
	/// </summary>
	[Serializable]
	public class Course : ISerializable
	{

		public String Name;
		public int Index;
		/// <summary>
		/// States if the data for this course has already been downloaded
		/// </summary>
		public Boolean DataRetrieved;

		/// <summary>
		/// States whether to download this course or not.
		/// </summary>
		public Boolean Download = false;

		public FolderItem Data;

		public Course(String a_sName, int a_iIndex)
		{
			Name = a_sName;
			Index = a_iIndex;

			Data = new FolderItem("Main");
		}

		//Deserialization constructor.
		public Course(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			Name = (String)a_oInfo.GetValue("Name", typeof(String));
			Index = (Int32)a_oInfo.GetValue("Index", typeof(Int32));
			DataRetrieved = (Boolean)a_oInfo.GetValue("DataRetrieved", typeof(Boolean));
			Data = (FolderItem)a_oInfo.GetValue("Data", typeof(FolderItem));
		}

		//Serialization function.
		public void GetObjectData(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			a_oInfo.AddValue("Name", Name);
			a_oInfo.AddValue("Index", Index);
			a_oInfo.AddValue("DataRetrieved", DataRetrieved);
			a_oInfo.AddValue("Data", Data, typeof(FolderItem));
		}

		public override String ToString()
		{
			return Name;
		}

		private FolderItem RecursiveGetFolderAt(String a_sPath, FolderItem a_oFolder)
		{
			String l_sFolderName;
			int l_iBackslashIndex = a_sPath.IndexOf('\\');

			// The API_Highlearn doesn't use \\, but a /. So I've added a special case
			if (l_iBackslashIndex < 0)
			{
				l_iBackslashIndex = a_sPath.IndexOf('/');
			}

			if (a_sPath.Length > 0)
			{
				// First assign the folder name (will be changed if needed afterwards
				l_sFolderName = a_sPath;

				// If there are still more subdirectories to dive into
				if (l_iBackslashIndex > -1)
				{
					l_sFolderName = a_sPath.Substring(0, l_iBackslashIndex);
					
					// Remove that backslash
					a_sPath = a_sPath.Remove(l_iBackslashIndex, 1);
				}

				// Remove the current folder name
				a_sPath = a_sPath.Remove(0, l_sFolderName.Length);

				// And set out to the next subdirectory
				return RecursiveGetFolderAt(a_sPath, a_oFolder.Folders[l_sFolderName]);
			}
			else
			{
				// We're here
				return a_oFolder;
			}
		}

		public FolderItem GetFolderAt(String a_sPath)
		{
			return RecursiveGetFolderAt(a_sPath, this.Data);
		}

		/// <summary>
		/// Counts all the files in the folder
		/// </summary>
		/// <param name="a_bDownloadEnabled">Count only the files with the download flag enabled</param>
		/// <returns></returns>
		public int CountFiles(Boolean a_bDownloadEnabled)
		{
			return Data.CountFiles(true, a_bDownloadEnabled);
		}

	}

	[Serializable]
	public class CDataCenter : ISerializable
	{

		public SortedList<String, Course> Courses;

		public CDataCenter()
		{
			Courses = new SortedList<string, Course>();
		}

		//Deserialization constructor.
		public CDataCenter(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			//Get the values from info and assign them to the appropriate properties
			Courses = (SortedList<String, Course>)a_oInfo.GetValue("Courses", typeof(SortedList<String, Course>));


		}

		//Serialization function.
		public void GetObjectData(SerializationInfo a_oInfo, StreamingContext a_oContext)
		{
			a_oInfo.AddValue("Courses", Courses, typeof(SortedList<String, Course>));
		}

		/// <summary>
		/// A Temporary function. It is used to parse the hashtable into the courses structure
		/// </summary>
		/// <param name="a_oHash"></param>
		public void Parse(System.Collections.Hashtable a_oHash)
		{
			int Index = 0;
			foreach (string key in a_oHash.Keys)
			{
				if (!Courses.ContainsKey(key))
				{
					// Key = Course Name
					// Index = Course Number
					Hashtable node_folder = (Hashtable)a_oHash[key];
					Index = (int)node_folder["_index"];

					Courses.Add(key, new Course(key, Index));
				}
			}
		}

		/// <summary>
		/// Creating a file withing the folder structure, creating all folders if needed
		/// </summary>
		/// <param name="a_oFolder"></param>
		/// <param name="a_sPath">A list of folders to create</param>
		/// <param name="a_sFilename"></param>
		private void MakeFilename(FolderItem a_oFolder, List<String> a_sPath, String a_sFilename)
		{
			String l_sFolderName;
			FolderItem l_oNewFolder;

			if (a_sPath.Count > 0)
			{
				l_sFolderName = a_sPath[0];
				a_sPath.RemoveAt(0);

				if (a_oFolder.Folders.ContainsKey(l_sFolderName))
				{
					l_oNewFolder = a_oFolder.Folders[l_sFolderName];
				}
				else
				{
					l_oNewFolder = new FolderItem(l_sFolderName);
					a_oFolder.Folders.Add(l_sFolderName, l_oNewFolder);
				}

				MakeFilename(l_oNewFolder, a_sPath, a_sFilename);
			}
			else
			{
				// We're here!
				a_oFolder.Files.Add(a_sFilename, new FileItem(a_sFilename));
			}
		}

		#region Parsing Methods
		private void ParseInnerFolder(FolderItem a_oFolder, Hashtable a_oHash)
		{
			Hashtable l_oHashFolder;
			FolderItem l_oNewFolder;

			foreach (string Key in a_oHash.Keys)
			{
				// Ignore the first index folder
				if (Key == "_index")
					continue;

				l_oHashFolder = (Hashtable)a_oHash[Key];
			
				l_oNewFolder = new FolderItem(Key);
				a_oFolder.Folders.Add(Key, l_oNewFolder);
				
				ParseInnerFolder(l_oNewFolder, l_oHashFolder);
			}
		}

		public void ParseFolders(String a_sCourseName, List<Miner_Highlearn.HLItem> a_oItems)
		{
			List<String> l_oSplittedFolders;

			//TestingUtilites.SaveHLItems(a_oItems, "Dump.dump");

			foreach (Miner_Highlearn.HLItem l_oItem in a_oItems)
			{
				// Split the folder names
				l_oSplittedFolders = new List<String>();
				l_oSplittedFolders.AddRange(l_oItem.Path.Split('/'));

				// And start recursing
				MakeFilename(Courses[a_sCourseName].Data, l_oSplittedFolders, l_oItem.Name);
			}

			//ParseInnerFolder(Courses[a_sCourseName].Data, a_oHash);
			Courses[a_sCourseName].DataRetrieved = true;
		}

		public void ParseAll(System.Collections.Hashtable a_oHash)
		{
			// parses all the tree (From GetCourseInformation)
		}
		#endregion
	}
}
